﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SafClassDefinition.cs" company="Collaboris Ltd.">
//   Copyright (c) Collaboris Ltd. All rights Reserved.
// </copyright>
// <summary>
//   
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Collaboris.Saf.Definitions
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Xml;

    using Converters.Spring;

    using SharePoint.Wss.Attributes;

    using Utilities;

    using Utils;


    /// <summary>
    /// </summary>
    [Serializable]
    public class SafClassDefinition
    {
        #region Constants and Fields

        /// <summary>
        /// </summary>
        private string @alias;

        /// <summary>
        /// </summary>
        private string assembly;

        /// <summary>
        /// </summary>
        private string assemblyQualifiedName;

        /// <summary>
        /// </summary>
        private string className;

        /// <summary>
        /// </summary>
        private string contactAuthorEmail;

        /// <summary>
        /// </summary>
        private string full4PartName;

        /// <summary>
        /// </summary>
        private string fullAssemblyName;

        /// <summary>
        /// </summary>
        private string fullName;

        /// <summary>
        /// </summary>
        private string genricTypeName;

        /// <summary>
        /// </summary>
        private string helpText;

        /// <summary>
        /// </summary>
        private bool isAction;

        /// <summary>
        /// </summary>
        private SafPropertyDefinitionCollection properties;

        /// <summary>
        /// </summary>
        private ReleaseStatus releaseStatus;

        /// <summary>
        /// </summary>
        private string remarks;

        /// <summary>
        /// </summary>
        private string title;

        /// <summary>
        /// </summary>
        private Type underlyingType;

        private TypeConverter _typeConverter;

        private bool _isGeneric;

        private SafClassDefinition _genericTypeClassDefinition;

        private Type _genericUnderlyingType;

        private Dictionary<string, SafClassDefinition> _genericTypeArguments;

        private string nameSpace;

        private bool undoImplemented;

        private string _classGuid;

        private SafPropertyDefinition parent;

        private string uniqueObjectId;

        private string[] supportFiles;

        #endregion

        #region Properties

        /// <summary>
        /// </summary>
        public string Alias
        {
            get
            {
                if (this.alias == null)
                {
                    if (this.IsGeneric)
                    {
                        string alias = string.Empty;
                    }
                    else
                        this.alias = ActionReflector.GetAlias(this.GetType());


                }

                return this.alias;
            }

            set
            {
                this.alias = value;
            }
        }

        /// <summary>
        /// </summary>
        public string AssemblyCodebaseLocation
        {
            get
            {
                return this.assemblyQualifiedName;
            }

            set
            {
                this.assemblyQualifiedName = value;
            }
        }

        /// <summary>
        /// </summary>
        public string AssemblyFullName
        {
            get
            {
                return this.fullAssemblyName;
            }

            set
            {
                this.fullAssemblyName = value;
            }
        }

        /// <summary>
        /// </summary>
        public string AssemblyName
        {
            get
            {
                return this.assembly;
            }

            set
            {
                this.assembly = value;
            }
        }

        /// <summary>
        /// </summary>
        public string ClassName
        {
            get
            {
                return this.className;
            }

            set
            {
                this.className = value;
            }
        }

        /// <summary>
        /// </summary>
        public string ContactAuthorEmail
        {
            get
            {
                return this.contactAuthorEmail;
            }

            set
            {
                this.contactAuthorEmail = value;
            }
        }

        /// <summary>
        /// </summary>
        public string Full4PartName
        {
            get
            {
                return this.full4PartName;
            }

            set
            {
                this.full4PartName = value;
            }
        }

        /// <summary>
        /// </summary>
        public string FullName
        {
            get
            {
                return this.fullName;
            }

            set
            {
                this.fullName = value;
            }
        }

        /// <summary>
        /// </summary>
        public bool IsAction
        {
            get
            {
                return this.isAction;
            }

            set
            {
                this.isAction = value;
            }
        }

        /// <summary>
        /// </summary>
        public SafPropertyDefinitionCollection Properties
        {
            get
            {
                if (this.properties == null)
                {
                    this.properties = new SafPropertyDefinitionCollection();
                }

                return this.properties;
            }

            set
            {
                this.properties = value;
            }
        }

        /// <summary>
        /// </summary>
        public ReleaseStatus ReleaseStatus
        {
            get
            {
                return this.releaseStatus;
            }

            set
            {
                this.releaseStatus = value;
            }
        }

        /// <summary>
        /// </summary>
        public string Remarks
        {
            get
            {
                return this.remarks;
            }

            set
            {
                this.remarks = value;
            }
        }

        /// <summary>
        /// </summary>
        public string Summary
        {
            get
            {
                return this.helpText;
            }

            set
            {
                this.helpText = value;
            }
        }

        /// <summary>
        /// </summary>
        public string Title
        {
            get
            {
                return this.title;
            }

            set
            {
                this.title = value;
            }
        }

        /// <summary>
        /// </summary>
        public Type UnderlyingType
        {
            get
            {
                return this.underlyingType;
            }

            set
            {
                this.underlyingType = value;
            }
        }

        public TypeConverter TypeConverter
        {
            get
            {
                return _typeConverter;
            }
            set
            {
                _typeConverter = value;
            }
        }

        public bool IsGeneric
        {
            get
            {
                return _isGeneric;
            }
            set
            {
                _isGeneric = value;
            }
        }

        public string NameSpace
        {
            get
            {
                return this.nameSpace;
            }
            set
            {
                this.nameSpace = value;
            }
        }

        public bool UndoImplemented
        {
            get
            {
                return this.undoImplemented;
            }
            set
            {
                this.undoImplemented = value;
            }
        }

        public string ClassGuid
        {
            get
            {
                return _classGuid;
            }
            set
            {
                _classGuid = value;
            }
        }

        public SafPropertyDefinition Parent
        {
            get
            {
                return this.parent;
            }
            set
            {
                this.parent = value;
            }
        }

        #endregion

        public void AssignParent(SafPropertyDefinition parentPropertyDef)
        {
            foreach (KeyValuePair<string, SafPropertyDefinition> keyValuePair in this.Properties)
            {
                SafPropertyDefinition propertyDefinition = keyValuePair.Value;
                propertyDefinition.ParentPropertyDefinition = parentPropertyDef;

                // its a genric propery
                if (propertyDefinition.GenericTypeClassDefinition != null)
                {

                    foreach (
                        KeyValuePair<string, SafPropertyDefinition> genericPair in
                            propertyDefinition.GenericTypeClassDefinition.Properties)
                    {

                        foreach (
                            KeyValuePair<string, SafClassDefinition> pair in
                                propertyDefinition.GenericTypeArguments)
                        {
                            // could be null if we haven't documented it!!!!
                            if (pair.Value != null)
                                pair.Value.AssignParent(genericPair.Value);
                        }
                    }

                } // its a complex property
                
                if (propertyDefinition.ClassDefinition != null)
                {
                    propertyDefinition.ClassDefinition.AssignParent(propertyDefinition);
                }
            }

        }

        #region Spring Methods



        public string ToSpring()
        {
            return this.ToSpring(false);
        }

        public string ToSpring(bool renderAsTemplate)
        {
            return this.ToSpring(renderAsTemplate, true);
        }

        private string ToSpringWithoutHeader(bool renderAsTemplate, SafPropertyDefinition parentPropertyDef)
        {
            string springXml;
            string currentTypeName;

            try
            {
                springXml = @"<object id=""" + this.UniqueObjectId + @""" type=""" + this.Alias + @""" lazy-init=""true"">";

                foreach (KeyValuePair<string, SafPropertyDefinition> keyValuePair in this.Properties)
                {
                    SafPropertyDefinition propertyDefinition = keyValuePair.Value;
                    propertyDefinition.ParentPropertyDefinition = parentPropertyDef;

                    if (propertyDefinition.OutputProperty)
                    {
                        springXml += @"<property name=""" + propertyDefinition.Name + @""" ";
                        springXml += @" value=""#{" + propertyDefinition.Name + @"}"" />";
                    }
                    else
                    {
                        // its a genric propery
                        //if (propertyDefinition.GenericTypeClassDefinition != null)
                        //{
                        //    springXml += @"<property name=""" + propertyDefinition.Name + @""">";
                        //    springXml += @" <object type=""" + propertyDefinition.ClassDefinition.Alias + @""">";

                        //    foreach (
                        //        KeyValuePair<string, SafPropertyDefinition> genericPair in
                        //            propertyDefinition.GenericTypeClassDefinition.Properties)
                        //    {

                        //        springXml += @"<property name=""" + genericPair.Value.Name + @""">";
                        //        foreach (
                        //            KeyValuePair<string, SafClassDefinition> pair in
                        //                propertyDefinition.GenericTypeArguments)
                        //        {
                        //            // could be null if we haven't documented it!!!!
                        //            if (pair.Value != null)
                        //                springXml += pair.Value.ToSpringWithoutHeader(renderAsTemplate, genericPair.Value);
                        //        }
                        //        springXml += @"</property>";
                        //    }
                        //    springXml += @" </object>";
                        //    springXml += @"</property>";

                        //} // its a complex property
                        
                        if (propertyDefinition.ClassDefinition != null)
                        {
                            springXml += @"<property name=""" + propertyDefinition.Name + @""">";
                            springXml += propertyDefinition.ClassDefinition.ToSpringWithoutHeader(renderAsTemplate, propertyDefinition);
                            springXml += @"</property>";

                        }
                        else
                        {
                            currentTypeName = propertyDefinition.UnderlyingType.Name.ToUpper();
                            springXml += this.ConvertToSpring(propertyDefinition, currentTypeName, renderAsTemplate);
                        }
                    }
                }

                // get the Spring Xml and return back to client, ready to be embedded.
                springXml += "</object>";

                return springXml;
            }
            catch (Exception exception)
            {

                throw;
            }
        }

        protected string UniqueObjectId
        {
            get
            {

                this.uniqueObjectId = this.ClassName + NearlyUniqueString();

                uniqueObjectId = uniqueObjectId.Replace("`", "");

                return this.uniqueObjectId;
            }
            set
            {
                this.uniqueObjectId = value;
            }
        }

        public string[] SupportFiles
        {
            get
            {
                return this.supportFiles;
            }
            set
            {
                this.supportFiles = value;
            }
        }

        public string ToSpring(bool renderAsTemplate, bool escapeChars)
        {
            string macroXml = this.ToSpringWithoutHeader(renderAsTemplate, null);
            macroXml = SpringHeader() + macroXml + this.SpringFooter();

            if (escapeChars)
                return System.Security.SecurityElement.Escape(StringHelper.FormatXml(macroXml));

            return StringHelper.FormatXml(macroXml);
        }

        private string SpringHeader()
        {
            string springXml = @"<?xml version=""1.0"" encoding=""utf-8"" ?>" +
                               @"<objects xmlns=""http://www.springframework.net"" default-lazy-init=""true"">" +
                               @"<object id=""MacroToProcess"" type=""Macro"" >" +
                               @"<constructor-arg name=""templateid"" value=""{" + Guid.NewGuid() + @"}"" />" +
                               @"<constructor-arg name=""actions"">" +
                               @"<list element-type=""IAction"">";

            return springXml;
        }

        private string SpringFooter()
        {
            string springXml = @"</list>" + "</constructor-arg>" + "</object>" + "</objects>";

            return springXml;

        }

        #endregion

        #region MSBuild Methods

        private string ConvertToSpring(SafPropertyDefinition propertyDefinition, string currentTypeName, bool renderAsTemplate)
        {
            string springXml = string.Empty;

            switch (currentTypeName)
            {
                case "SERIALIZABLESTRINGDICTIONARY":
                    {
                        SerializableStringDictionaryConverter converter = new SerializableStringDictionaryConverter();
                        springXml += converter.Convert(propertyDefinition, renderAsTemplate);
                        break;
                    }
                case "SERIALIZABLEOBJECTDICTIONARY`1":
                    {
                        SerializableObjectDictionaryConverter converter = new SerializableObjectDictionaryConverter();
                        springXml += converter.Convert(propertyDefinition, renderAsTemplate);
                        break;
                    }
                default:
                    {
                        if (propertyDefinition.Name != "EntityList")
                        {
                            DefaultConverter converter = new DefaultConverter();
                            springXml += converter.Convert(
                                propertyDefinition, renderAsTemplate, propertyDefinition.RenderAsExpression);

                        }
                        break;
                    }
            }

            return springXml;
        }

        private string ConvertToCommandLineArg(SafPropertyDefinition propertyDefinition, string currentTypeName)
        {
            string xml = string.Empty;

            switch (currentTypeName)
            {
                case "SERIALIZABLEOBJECTDICTIONARY`1":
                    {
                        SerializableObjectDictionaryConverter converter = new SerializableObjectDictionaryConverter();
                        xml += converter.ConvertToCommandLineArg(propertyDefinition);
                        break;
                    }
                case "SERIALIZABLESTRINGDICTIONARY":
                    {
                        SerializableStringDictionaryConverter converter = new SerializableStringDictionaryConverter();
                        xml += converter.ConvertToCommandLineArg(propertyDefinition);
                        break;
                    }
                default:
                    {
                        if (propertyDefinition.Name != "EntityList")
                        {
                            DefaultConverter converter = new DefaultConverter();
                            xml += converter.ConvertToCommandLineArg(propertyDefinition);
                        }
                        break;
                    }
            }

            return xml;
        }

        private string ConvertToNameValue(SafPropertyDefinition propertyDefinition, string currentTypeName)
        {
            string xml = string.Empty;

            switch (currentTypeName)
            {
                case "SERIALIZABLEOBJECTDICTIONARY`1":
                    {
                        SerializableObjectDictionaryConverter converter = new SerializableObjectDictionaryConverter();
                        xml += converter.ConvertToKeyValuePair(propertyDefinition);
                        break;
                    }
                case "SERIALIZABLESTRINGDICTIONARY":
                    {
                        SerializableStringDictionaryConverter converter = new SerializableStringDictionaryConverter();
                        xml += converter.ConvertToKeyValuePair(propertyDefinition);
                        break;
                    }
                default:
                    {
                        if (propertyDefinition.Name != "EntityList")
                        {
                            DefaultConverter converter = new DefaultConverter();
                            xml += converter.ConvertToKeyValuePair(propertyDefinition);
                        }
                        break;
                    }
            }

            return xml;
        }

        private string ConvertToElement(SafPropertyDefinition propertyDefinition, string currentTypeName)
        {
            string xml = string.Empty;

            switch (currentTypeName)
            {
                case "SERIALIZABLEOBJECTDICTIONARY`1":
                    {
                        SerializableObjectDictionaryConverter converter = new SerializableObjectDictionaryConverter();
                        xml += converter.ConvertToXmlElement(propertyDefinition);
                        break;
                    }
                case "SERIALIZABLESTRINGDICTIONARY":
                    {
                        SerializableStringDictionaryConverter converter = new SerializableStringDictionaryConverter();
                        xml += converter.ConvertToXmlElement(propertyDefinition);
                        break;
                    }
                default:
                    {
                        if (propertyDefinition.Name != "EntityList")
                        {
                            DefaultConverter converter = new DefaultConverter();
                            xml += converter.ConvertToXmlElement(propertyDefinition);
                        }
                        break;
                    }
            }

            return xml;
        }

        public string ToMSBuild(bool wrapInItemGroup)
        {
            return this.ToMSBuild(wrapInItemGroup, true);
        }

        public string ToMSBuild(bool wrapInItemGroup, bool escapeChars)
        {
            string macroXml = this.ToMSBuildWithoutHeader(wrapInItemGroup);
            macroXml = this.MSBuildHeader() + macroXml + this.MSBuildFooter();

            XmlDocument doc = new XmlDocument();
            this.RemoveDuplicateNodesAndAddDefaults(macroXml, doc);

            macroXml = doc.OuterXml;

            if (escapeChars)
                return System.Security.SecurityElement.Escape(StringHelper.FormatXml(macroXml));

            return StringHelper.FormatXml(macroXml);
        }

        private void RemoveDuplicateNodesAndAddDefaults(string macroXml, XmlDocument doc)
        {
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
            nsmgr.AddNamespace("ms", "http://schemas.microsoft.com/developer/msbuild/2003");

            doc.LoadXml(macroXml);
            List<string> nodes = new List<string>();
            if (doc.DocumentElement != null)
            {
                XmlNode macrosNode = doc.DocumentElement.SelectSingleNode("/ms:Project/ms:ItemGroup/ms:Macros", nsmgr);
                for (int i = macrosNode.ChildNodes.Count - 1; i >= 0; i--)
                {
                    XmlNode node = macrosNode.ChildNodes[i];

                    if (nodes.Contains(node.Name))
                        node.ParentNode.RemoveChild(node);
                    else
                        nodes.Add(node.Name);
                }

                if (!nodes.Contains("Url"))
                {
                    XmlElement urlElement = doc.CreateElement("Url", "http://schemas.microsoft.com/developer/msbuild/2003");
                    urlElement.InnerText = "http://localhost:4422";
                    macrosNode.AppendChild(urlElement);
                }

                if (!nodes.Contains("InstanceId"))
                {
                    XmlElement instanceIdNode = doc.CreateElement("InstanceId", "http://schemas.microsoft.com/developer/msbuild/2003");
                    instanceIdNode.InnerText = "{" + this.ClassGuid + "}";
                    macrosNode.AppendChild(instanceIdNode);
                }
            }


        }

        public static string NearlyUniqueString()
        {
            return Guid.NewGuid().ToString().Substring(0, 2);
        }

        private void RemoveDuplicateAttributes(string macroXml, XmlDocument doc)
        {
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
            nsmgr.AddNamespace("ms", "http://schemas.microsoft.com/sharepoint/");
            try
            {
                doc.LoadXml(macroXml);
            }
            catch (Exception exception)
            {

                throw;
            }

            List<string> nodes = new List<string>();
            if (doc.DocumentElement != null)
            {
                XmlNode propsNode = doc.DocumentElement.SelectSingleNode("/ms:Feature/ms:Properties", nsmgr);
                for (int i = propsNode.ChildNodes.Count - 1; i >= 0; i--)
                {
                    XmlNode node = propsNode.ChildNodes[i];

                    if (node.Attributes != null && node.Attributes["Key"] != null)
                    {
                        if (nodes.Contains(node.Attributes["Key"].Value))
                            node.ParentNode.RemoveChild(node);
                        else
                            nodes.Add(node.Attributes["Key"].Value);
                    }
                }
            }
        }


        /// <summary>
        /// Toes the MS build without header.
        /// </summary>
        /// <param name="wrapInItemGroup">if set to <c>true</c> [wrap in item group].</param>
        /// <returns></returns>
        public string ToMSBuildWithoutHeader(bool wrapInItemGroup)
        {
            string msBuild = string.Empty;

            if (wrapInItemGroup)
                msBuild = @"<Macros Include=""" + this.FullName + @".macroTemplate"">";

            string currentTypeName = string.Empty;

            foreach (KeyValuePair<string, SafPropertyDefinition> keyValuePair in this.Properties)
            {
                SafPropertyDefinition propertyDefinition = keyValuePair.Value;
                if (propertyDefinition.OutputProperty)
                {
                    // do nothing for now as we don't handle output props back to MSBuild
                    // one day....there will be code here to do that.
                }
                else
                {
                    if (propertyDefinition.ClassDefinition != null)
                    {
                        msBuild += propertyDefinition.ClassDefinition.ToMSBuildWithoutHeader(false);
                    }
                    else
                    {
                        currentTypeName = propertyDefinition.UnderlyingType.Name.ToUpper();
                        msBuild += this.ConvertToElement(propertyDefinition, currentTypeName);
                    }
                }
            }

            // get the Spring Xml and return back to client, ready to be embedded.
            if (wrapInItemGroup)
                msBuild += "</Macros>";

            return msBuild;
        }

        private string MSBuildHeader()
        {
            string msbuildXml = @"<?xml version=""1.0"" encoding=""utf-8""?>" +
                                @"<!-- Defines a 'SAF Macro' containiing 'Actions' to be run on 'Do' and 'Undo' -->" +
                                @"<!-- Please visit http://www.collaboris.co.uk/Saf/Doc/default.html for more information. -->" +
                                @"<Project DefaultTargets=""Do"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">" +
                                @"<UsingTask AssemblyName=""Collaboris.Saf, Version=2.0.0.0, Culture=neutral, PublicKeyToken=182db3eac6a9e195"" TaskName=""Collaboris.Saf.Adapters.MSBuild.SAF""/>" +
                                @"<ItemGroup>";

            return msbuildXml;
        }

        private string MSBuildFooter()
        {
            string msbuildXml = @"</ItemGroup>" +
                                @"<Target Name=""Do"">" +
                                @"<SAF MacroPaths=""@(Macros)"" ProcessMode=""Do"" ContinueOnError=""false"" />" + "</Target>" +
                                @"<Target Name=""Undo"">" +
                                @"<SAF MacroPaths=""@(Macros)"" ProcessMode=""Undo"" ContinueOnError=""false"" />" + "</Target>" +
                                "</Project>";

            return msbuildXml;
        }

        #endregion

        #region Feature Methods

        /// <summary>
        /// Toes the MS build without header.
        /// </summary>
        /// <returns></returns>
        public string ToFeatureWithoutHeader()
        {
            string featureXml = string.Empty;

            string currentTypeName = string.Empty;

            foreach (KeyValuePair<string, SafPropertyDefinition> keyValuePair in this.Properties)
            {
                SafPropertyDefinition propertyDefinition = keyValuePair.Value;
                if (propertyDefinition.OutputProperty)
                {
                    // do nothing for now as we don't handle output props back to MSBuild
                    // one day....there will be code here to do that.
                }
                else
                {
                    //if (propertyDefinition.GenericTypeClassDefinition != null)
                    //{
                    //    foreach (KeyValuePair<string, SafPropertyDefinition> genericPair in
                    //        propertyDefinition.GenericTypeClassDefinition.Properties)
                    //    {

                    //        foreach (
                    //            KeyValuePair<string, SafClassDefinition> pair in propertyDefinition.GenericTypeArguments
                    //            )
                    //        {
                    //            if (pair.Value != null)
                    //                featureXml += pair.Value.ToFeatureWithoutHeader();
                    //        }
                    //    }
                    //}

                    if (propertyDefinition.ClassDefinition != null)
                    {
                        featureXml += propertyDefinition.ClassDefinition.ToFeatureWithoutHeader();
                    }
                    else
                    {
                        currentTypeName = propertyDefinition.UnderlyingType.Name.ToUpper();
                        featureXml += this.ConvertToNameValue(propertyDefinition, currentTypeName);
                    }
                }
            }

            return featureXml;
        }


        public string ToFeature(bool escapeChars)
        {

            string featureXml = this.ToFeatureWithoutHeader();
            string xml = this.FeatureXmlHeader() + featureXml + this.FeatureXmlFooter();

            XmlDocument doc = new XmlDocument();
            this.RemoveDuplicateAttributes(xml, doc);

            xml = doc.OuterXml;

            if (escapeChars)
                return System.Security.SecurityElement.Escape(StringHelper.FormatXml(xml));

            return StringHelper.FormatXml(xml);
        }

        private string FeatureXmlHeader()
        {
            string featureXmlHeader = @"<?xml version=""1.0"" encoding=""utf-8""?>" + Environment.NewLine +
                                @"<Feature  Id=""" + this.ClassGuid + @""" " + Environment.NewLine +
                                @"Title=""SAF Sample Feature - '" + this.Alias + @"'"" " + Environment.NewLine +
                                @"Description=""Sample Feature illustrating how to call and run '" + this.ClassName + @"' from a SAF Macro."" " + Environment.NewLine +
                                @"Version=""12.0.0.0"" " + Environment.NewLine +
                                @"Hidden=""FALSE""  " + Environment.NewLine +
                                @"Scope=""Web"" " + Environment.NewLine +
                                @"DefaultResourceFile=""core"" " + Environment.NewLine +
                                @"xmlns=""http://schemas.microsoft.com/sharepoint/"" " + Environment.NewLine +
                                @"ReceiverAssembly=""Collaboris.Saf, Version=2.0.0.0, Culture=neutral, PublicKeyToken=182db3eac6a9e195"" " + Environment.NewLine +
                                @"ReceiverClass=""Collaboris.Saf.Adapters.SafFeatureReceiver"">" + Environment.NewLine +
                                @"<Properties>"  + Environment.NewLine ;

            featureXmlHeader += @"<!-- Defines a SAF Macro containiing Actions to be run on Do and Undo -->" + Environment.NewLine +
                                @"<!-- Please visit http://www.collaboris.co.uk/Saf/Doc/default.html for more information. -->" + Environment.NewLine +
                                @"  " + Environment.NewLine +
                                @"<Property Key=""MacroFile"" Value=""" + this.FullName + @".macroTemplate"" />" + Environment.NewLine +
                                @"<Property Key=""InstanceId"" Value=""" + this.ClassGuid + @""" />";


            return featureXmlHeader;
        }

        private string FeatureXmlFooter()
        {
            string msbuildXml = @"<!-- You can also define your own properties to be resolved by PlaceHolders in the Macro ... <Property Key=""YourKey"" Value=""YourValue"" /> -->" +
                                @"</Properties>" + @"</Feature>";

            return msbuildXml;
        }

        #endregion

        #region StsAdm Methods

        /// <summary>
        /// Toes the STS adm without header.
        /// </summary>
        /// <returns></returns>
        public string ToStsAdmWithoutHeader()
        {
            string stsXml = string.Empty;

            string currentTypeName = string.Empty;

            foreach (KeyValuePair<string, SafPropertyDefinition> keyValuePair in this.Properties)
            {
                SafPropertyDefinition propertyDefinition = keyValuePair.Value;
                if (propertyDefinition.OutputProperty)
                {
                    // do nothing for now as we don't handle output props back to MSBuild
                    // one day....there will be code here to do that.
                }
                else
                {
                    if (propertyDefinition.ClassDefinition != null)
                    {
                        stsXml += propertyDefinition.ClassDefinition.ToStsAdmWithoutHeader();
                    }
                    else
                    {
                        currentTypeName = propertyDefinition.UnderlyingType.Name.ToUpper();
                        stsXml += this.ConvertToCommandLineArg(propertyDefinition, currentTypeName);
                    }
                }
            }

            return stsXml;
        }


        public string ToStsAdm(bool escapeChars)
        {

            string stsXml = this.ToStsAdmWithoutHeader();
            string batchFile = this.StsAdmHeader()+ stsXml + this.StsAdmFooter();

            if (escapeChars)
                return System.Security.SecurityElement.Escape(batchFile);

            return batchFile;
        }

        private string StsAdmHeader()
        {
            string stsAdmHeader =    "REM Defines a SAF Macro containiing Actions to be run on Do and/or Undo " + Environment.NewLine +
                                    @"REM Please visit http://www.collaboris.co.uk/Projects/SafWiki/SAF_WIKI/Contents_Page.aspx for more information. " + Environment.NewLine +
                                    @"REM IMPORTANT! If this macro needs a SharePoint context please amend the '-url http://localhost:4422' argument (below)." + Environment.NewLine +
                                    @"REM				 " + Environment.NewLine +
                                    @"REM            If the macro DOESN'T require a SharePoint context, simply remove the '-url http://localhost:4422' argument. " + 
                                    Environment.NewLine +
                                    @"SET STSADM=C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\bin\stsadm.exe" + Environment.NewLine +
                                    Environment.NewLine +
                                    @"""%STSADM%"" -o saf-run -macrofile " + this.FullName + @".macroTemplate -mode ""do"" -url http://localhost:4422 " ;
                                
            return stsAdmHeader;
        }

        private string StsAdmFooter()
        {
            string stsAdmFooter = @"";

            return stsAdmFooter;
        }

        #endregion

        #region Unit Tests

        private string ToUnitTestWithoutHeader()
        {
            string method = @"using {3};" +
                            @"  " + Environment.NewLine + Environment.NewLine +
                            @"  [TestClass]" + Environment.NewLine +
                            @"  public class {0}Test" + Environment.NewLine +
                            @"  {{" + Environment.NewLine +
                            @"  " + Environment.NewLine +
                            @"      [TestMethod] " + Environment.NewLine +
                            @"      public void {0}_Test() " + Environment.NewLine +
                            @"      {{ " + Environment.NewLine +
                            @"          TestAssistant<{0}, {1}> {2} = " + Environment.NewLine +
                            @"                  new TestAssistant<{0}, {1}>(TestEnvironment.PublishingSite, ""{0}TestWeb"", ""BLANKINTERNET#0""); " + Environment.NewLine + Environment.NewLine +
                            @"          using ({2}) " + Environment.NewLine +
                            @"          {{ " + Environment.NewLine +
                            @"              MacroReport doReport = {2}.DoMacro(); " + Environment.NewLine + Environment.NewLine;
            
            if (this.UndoImplemented)
            {
                method += @"              MacroReport unDoReport = {2}.UndoMacro(); " + Environment.NewLine; 

            }

                method +=   @"          }} " + Environment.NewLine +
                            @"      }} " + Environment.NewLine + Environment.NewLine ;

            string entityName = string.Empty;
            foreach (KeyValuePair<string, SafPropertyDefinition> key in this.Properties)
            {
                if (key.Value.Name == "EntityList")
                {
                    if (key.Value.ClassDefinition != null)
                    {
                        if (key.Value.GenericTypeFullName != null)
                        {
                            entityName = key.Value.GenericTypeFullName;
                            if (key.Value.GenericTypeArguments != null)
                            {
                                entityName += "<";
                                foreach(KeyValuePair<string, SafClassDefinition> key1 in key.Value.GenericTypeArguments)
                                {
                                    entityName += key1.Key;
                                }
                                entityName += ">";
                            }
                        }else
                        {
                            entityName = key.Value.ClassDefinition.ClassName;
                        }
                    }
                    else
                    {
                        entityName = key.Value.UnderlyingType.Name;
                    }
                }
            }

            method = String.Format(method, this.ClassName, entityName, this.ClassName.ToLower(), this.NameSpace);

            return method;
        }

        /// <summary>
        /// PLEASE DO NOT USE THIS - FOR DOCUMENTATION / BUILD PURPOSES ONLY
        /// </summary>
        /// <returns></returns>
        public string ToUnitTest()
        {

            string stsXml = this.ToUnitTestWithoutHeader();
            string classFile = this.UnitTestHeader()+ stsXml + this.UnitTestFooter();
            classFile = classFile.Replace("}}", "}");
            classFile = classFile.Replace("{{", "{");
            
            return classFile;
        }

        private string UnitTestHeader()
        {
            string unitTestHeader = @"namespace ##namespace##" + Environment.NewLine +
                                    @"{{" + Environment.NewLine +
                                    @"  using Engine.Report;" + Environment.NewLine +
                                    @"  using Microsoft.SharePoint;" + Environment.NewLine +
                                    @"  using Microsoft.VisualStudio.TestTools.UnitTesting;" + Environment.NewLine +
                                    @"  using Saf.Tests;" + Environment.NewLine +
                                    @"  using ##entity_namespace##;" + Environment.NewLine ;
                                    
                                
            return unitTestHeader;
        }

        private string UnitTestFooter()
        {
            string unitTestFooter = @"  }} " + Environment.NewLine +
                                      @"}}" + Environment.NewLine ;

            return unitTestFooter;
        }
         
        #endregion
    }
}